'use strict';

/**
 * alloyteamer
 * github: 
 * @author joeyguo
 * version: 0.1.0
 * date: 
 *  
 * MIT license
 */

// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating

// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel

(function () {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame) window.requestAnimationFrame = function (callback, element) {
        var currTime = new Date().getTime();
        var timeToCall = Math.max(0, 16 - (currTime - lastTime));
        var id = window.setTimeout(function () {
            callback(currTime + timeToCall);
        }, timeToCall);
        lastTime = currTime + timeToCall;
        return id;
    };

    if (!window.cancelAnimationFrame) window.cancelAnimationFrame = function (id) {
        clearTimeout(id);
    };
})();

class AlloyTimer {
	constructor() {
	    let self = this;
		this._id = 0;
	    this._queue = [];
        this._tmpqueue = [];
	    this.ticking = false;
	}

    /**
    * util
    * @private
    */
    _getItem(id, src) {
        var t = {};
        (src || this._queue).map((item, i) => {
            if (item._id === id) {
                t = item;
                t.index = i;
            }
        });
        return t;
    }

	/**
	* tick
	* @private
	*/
	_tick() {
		let self = this;

		if (self._queue.length === 0) {
			self.ticking = false;

		} else if(!self.ticking) {
			self.ticking = true;

			(function tick(){
				// console.log("60帧");
				for (var i = 0, item; i < self._queue.length; i++) {
					item = self._queue[i];

                    var _now       = Date.now(),
                        start      = item.start,
                        pausetime  = item.pausetime || 0,
						timestamps = item.timestamps,
						count      = item.count,
						callback   = item.callback,

						_left      = _now - start - pausetime; // 距离一开始的时间

				    if (_left >= (count + 1) * timestamps) {
				    	// 防止是延迟了多次区间，切换浏览器的tab时，requestAnimationFrame 不会被执行
				    	item.count = timestamps === 0? (++item.count): parseInt((_left / timestamps),10);

						// var offset = _now - (start + item.count * timestamps);
						// console.log("offset", offset);

						callback && callback(item.count, _left);
						if (item.times !== undefined) {
							item.times = item.times > 0? --(item.times): 0;

							if (item.times === 0) {
								self._remove(item._id);
							}
						}
					}
				}

				self.ticking && window.requestAnimationFrame(tick);
			})();
		}
	}

	/**
	* _add callbackFn
	* @private
	*/
   	_add(callback, timestamps, times) {
    	var id = this._id++;

    	this._queue.push({
			_id: id,
			start: Date.now(),
			timestamps: timestamps,
			callback: callback,
			count: 0,
            origintimes: times,
			times: times
		});

		if (times === 1) {
			var start = new Date().getTime();
			// 无阻塞时，原生 setTimeout 更快  增加 timeoutId
	    	this._queue[this._queue.length - 1].timeoutId = setTimeout(() => {
                var _left = new Date().getTime() - (start + timestamps);
	     		this._queue.map((item, i) => {
	     			item._id === id && callback && callback(++this._queue[i].count, _left); 
				});
                this._remove(id);
			},timestamps);
		}

		this._tick();

		return {
            clear: () => this._remove(id),
            stop: () => this._remove(id),
            reset: () => this._reset(id),
            pause: () => this._pause(id),
            play: () => this._play(id),
        };
    }

    /**
	* _remove callbackFn
	* @private
	*/
    _remove(id) {
        let item = this._getItem(id);
        let timeoutId = item.timeoutId;
        timeoutId && clearTimeout(timeoutId);
        this._queue.splice(item.index, 1);

		this._tick();
    }

    /**
    * _pause 
    * @private
    */
    _pause(id) {
        let item = this._getItem(id);
        let t = (this._queue.splice(item.index, 1) || [])[0];
        t && (t.stoptime = Date.now());
        t && (this._tmpqueue.push(t));
    }

    /**
    * _play 
    * @private
    */
    _play(id) {
        let item = this._getItem(id, this._tmpqueue);
        let t = (this._tmpqueue.splice(item.index, 1) || [])[0];
        t && (t.pausetime = Date.now() - t.stoptime + (t.pausetime || 0));
        t && (t.stoptime = 0);
        this._queue.push(t);
    }

    /**
    * _reset 
    * @private
    */
    _reset(id) {
        let item = this._getItem(id);
        item.start = Date.now();
        item.count = 0;
        item.times = item.origintimes;
        item.pausetime = 0;
        item.stoptime = 0;
        item.callback && item.callback(0, 0);
    }

    /**
	* requestInterval
	* @publish
	*/
    requestInterval(callback, timestamps, isAutostart){
    	isAutostart && callback && callback(0, 0);
    	return this._add(callback, timestamps)
    }

    /**
	* requestTimeout
	* @publish
	*/
    requestTimeout(callback, timestamps){
    	return this._add(callback, timestamps, 1);
    }

    /**
    * requestFrame
    * @publish
    */
    requestFrame(callback){
        return this._add(callback, 0);
    }
}

let T = new AlloyTimer();

export default AlloyTimer;
export const requestInterval  = T.requestInterval.bind(T);
export const requestTimeout  = T.requestTimeout.bind(T);
export const requestFrame   = T.requestFrame.bind(T);


if (window) {
	window.AlloyTimer   = AlloyTimer;
	window.requestInterval  = requestInterval;
    window.requestTimeout   = requestTimeout;
	window.requestFrame   = requestFrame;
};